﻿using QuickVideoLib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace QuickMediaPlayerDemo
{
    /// <summary>
    /// 播放器的一个界面实现
    /// </summary>
    public partial class QuickPlayer : UserControl, ID3DPosProvider
    {
        #region Private members


        private EasyMediaPlayer _player; //底层播放器类

        private D3DImage _d3dImage; //用于d3d显示方式
        private WriteableBitmap _videoBitmap; //用于WriteableBitmap显示方式
        private Int32Rect _frameRect; //当前图像区域
        private int _frameSize; //图像占内存大小
        private int _frameStride; //图像行跨距（宽度*3）

        private QuickTimer _updateTimer; //进度更新定时器
        private bool _isInTimerChange; //表示定时器修改了时间进度，而非拖动修改的
        private int _lastChangePosTick; //上次修改进度的时间

        //播放速率列表
        private List<string> _seedRates = new List<string>() { "0.5", "0.75", "1.0", "1.25", "1.5", "1.75", "2.0" };

        //跳帧列表
        private List<int> _jumpSteps = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        //图像缩放比率列表
        private List<string> _scales = new List<string>() { "1", "0.75", "0.5", "0.25", "0.125" };
        private readonly HashSet<string> PictureFormats = new HashSet<string>() { "bmp", "jpg", "jpeg", "gif", "tif", "tiff", "tga", "png", "webp", "ico" };

        private string _currentMediaUrl;
        private FormatInfo _curFormatInfo;
        #endregion

        #region Event

        //媒体变更事件
        public event EventHandler<MediaChangedEventArgs> MediaChanged;

        #endregion

        #region Constructor

        public QuickPlayer()
        {
            InitializeComponent();

            //是否使用D3D显示模式(D3D模式下，显卡压力大，CPU压力小)
            bool useD3dVideo = false;
            if (useD3dVideo)
            {
                _d3dImage = new D3DImage();
            }
            //D3D模式下需要实时报告显示位置，以便在多显卡机器上跨屏拖动时，底层切换显卡
            _player = new EasyMediaPlayer(useD3dVideo ? this : null);
            _player.VideoFrameReceived += _player_VideoFrameReceived;
            _player.D3DVideoFrameReceived += _player_D3DVideoFrameReceived;
            _player.StatusChanged += _player_StatusChanged;

            _updateTimer = new QuickTimer();//QuickTimer可立即执行一次，然后再周期执行
            _updateTimer.Interval = TimeSpan.FromMilliseconds(500);
            _updateTimer.Elapsed += UpdateTimer_Elapsed;

            cbxSpeedRates.ItemsSource = _seedRates;
            cbxSpeedRates.SelectedIndex = 2;

            cbxFrameJump.ItemsSource = _jumpSteps;
            cbxFrameJump.SelectedIndex = 0;

            cbxScaleRates.ItemsSource = _scales;
            cbxScaleRates.SelectedIndex = 0;
        }

        #endregion

        #region Private methods
        private string TimeSpanToStr(TimeSpan ts)
        {
            string res = $"{ts.Minutes:D02}:{ts.Seconds:D02}";
            if (ts.Hours > 0)
            {
                res = ts.Hours.ToString("D02") + ":" + res;
            }
            return res;
        }

        private bool IsPictureFormat()
        {
            try
            {
                string ext = Path.GetExtension(_currentMediaUrl).ToLower().TrimStart('.');
                return PictureFormats.Contains(ext);
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region 实现显卡汇报接口

        public QPoint ReportScreenPoint()
        {
            Point pt = videoImg.PointToScreen(new Point(0, 0));
            QPoint retPt = new QPoint();
            retPt.X = (int)pt.X;
            retPt.Y = (int)pt.Y;
            return retPt;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// D3D视频事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _player_D3DVideoFrameReceived(object sender, D3DVideoEventArgs e)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                if (_d3dImage == null)
                {
                    _d3dImage = new D3DImage();
                }
                if (videoImg.Source == null)
                {
                    videoImg.Source = _d3dImage;
                }
                _d3dImage.Lock();

                _d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, e.Surface);


                _d3dImage.AddDirtyRect(new Int32Rect(0, 0, e.Width, e.Height));
                _d3dImage.Unlock();
            }));
        }

        /// <summary>
        /// 状态处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _player_StatusChanged(object sender, StatusEventArgs e)
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                if (e.NewStatus == MpStatus.MP_STATUS_OPENED)
                {
                    //媒体打开成功后的处理
                    slider.IsHitTestVisible = _player.IsSeekable; //媒体不支持seek时禁止点击进度条
                    slider.Visibility = _player.IsRealTime ? Visibility.Hidden : Visibility.Visible; //直播时不显示进度条
                    slider.IsEnabled = true;
                    btnNextFrame.IsEnabled = _player.HasVideo && _player.CanPause; //只有视频且支持暂停时，才启用下一帧按钮
                    chkUseKeyFrameSeek.IsChecked = _player.UseKeyFrameSeek;  //获取是否使用关键帧定位
                    cbxSpeedRates.IsEnabled = !_player.IsRealTime; //直播源不支持变速

                    _updateTimer.Start();  //启动进度刷新定时器
                    _curFormatInfo = _player.FormatInfo;

                    if (!_player.HasVideo)
                    {
                        videoImg.Source = null;
                        _videoBitmap = null;
                    }
                }
                else if (e.NewStatus == MpStatus.MP_STATUS_STOPPED)
                {
                    //如果是打开失败触发的停止事件，那么不更新界面
                    if (e.OldStatus != MpStatus.MP_STATUS_OPENNING)
                    {
                        //停止后的界面处理
                        _updateTimer.Stop();
                        btnPlay.Content = "播放";
                        btnPlay.IsEnabled = true;
                        if (!IsPictureFormat())
                        {
                            videoImg.Source = null;
                            _videoBitmap = null;
                        }
                        ucStartTime.Content = "";
                        ucEndTime.Content = "";
                        slider.Value = 0;
                        slider.IsEnabled = false;
                        tbkFps.Text = "0.0";
                        cbxSpeedRates.IsEnabled = true;
                    }
                }
                else if (e.NewStatus == MpStatus.MP_STATUS_PLAYING)
                {
                    btnPlay.Content = _player.CanPause ? "暂停" : "播放中";
                    btnPlay.IsEnabled = _player.CanPause;
                }
                else if (e.NewStatus == MpStatus.MP_STATUS_PAUSED)
                {
                    btnPlay.Content = "播放";
                    btnPlay.IsEnabled = true;
                }
                tbkStatus.Text = e.NewStatus.ToString();
            }));
        }

        /// <summary>
        /// 视频帧事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _player_VideoFrameReceived(object sender, VideoEventArgs e)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                //当图像大小变更或格式变更后，需要重新创建WriteableBitmap
                PixelFormat format = e.HasAlpha ? PixelFormats.Bgra32 : PixelFormats.Bgr24;
                int pixelLen = e.HasAlpha ? 4 : 3;
                if (_videoBitmap == null || _videoBitmap.PixelWidth != e.Width || _videoBitmap.Height != e.Height
                  || _videoBitmap.Format != format)
                {
                    _videoBitmap = new WriteableBitmap(e.Width, e.Height, 96, 96, format, null);
                    _frameRect = new Int32Rect(0, 0, e.Width, e.Height);
                    _frameSize = e.Width * e.Height * pixelLen;
                    _frameStride = e.Width * pixelLen;
                    videoImg.Source = _videoBitmap;
                }
                _videoBitmap.WritePixels(_frameRect, e.VideoData, _frameSize, _frameStride);
            }));
        }

        /// <summary>
        /// 文件拖放到界面上的处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void UserControl_Drop(object sender, DragEventArgs e)
        {

            try
            {
                var filePath = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();

                await _player.StopAsync();
                _currentMediaUrl = filePath;
                SetMultiParams();
                await _player.OpenPlay(filePath);

                tbkDragTip.Visibility = Visibility.Collapsed;
                MediaChanged?.Invoke(this, new MediaChangedEventArgs(filePath));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 时间刷新
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateTimer_Elapsed(object sender, EventArgs e)
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                //对于使用多次的值，先取出值，避免每次调用api
                TimeSpan curPos = _player.Position;
                TimeSpan duration = _player.NaturalDuration;
                slider.Maximum = duration.TotalMilliseconds;
                if (!slider.IsDragDelta)
                {
                    _isInTimerChange = true;//标记定时器改的进度条，避免调用底层的进度修改形成死循环
                    //手动调整后，给与500毫秒的保护期
                    if (Environment.TickCount - _lastChangePosTick > 500)
                        slider.Value = curPos.TotalMilliseconds; //刷新进度
                    _isInTimerChange = false;
                }
                ucStartTime.Content = TimeSpanToStr(curPos);//当前时间
                ucEndTime.Content = TimeSpanToStr(duration);//总时间
                tbkFps.Text = _player.Fps.ToString("F01"); //帧率
                tbkSize.Text = $"{_player.OutputVideoWidth} x {_player.OutputVideoHeight}"; //图像大小
            }));
        }

        /// <summary>
        /// 移动时间轴的处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            //如果时定时器更新UI造成的值变更不调用底层
            if (_isInTimerChange)
            {
                return;
            }
            _lastChangePosTick = Environment.TickCount;
            TimeSpan pos = TimeSpan.FromMilliseconds(e.NewValue);
            _player.SetPosition(pos);
        }

        /// <summary>
        /// 音量调节
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void volSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (IsLoaded && _player != null)
            {
                _player.Volumn = e.NewValue;
            }
        }

        /// <summary>
        /// 播放速率调节
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbxSpeedRates_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (IsLoaded && _player != null)
            {
                _player.SpeedRatio = Convert.ToDouble(cbxSpeedRates.SelectedItem.ToString());
            }
        }
        /// <summary>
        /// 跳帧播放
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbxFrameJump_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (IsLoaded && _player != null)
            {
                _player.FrameJumpStep = (int)cbxFrameJump.SelectedItem;
            }
        }

        /// <summary>
        /// 图像缩放
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbxScaleRates_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (IsLoaded && _player != null)
            {
                _player.ScaleFactor = (float)Convert.ToDouble(cbxScaleRates.SelectedItem);
            }
        }

        private void SetMultiParams()
        {
            _player.SpeedRatio = Convert.ToDouble(cbxSpeedRates.SelectedItem.ToString()); //根据UI设置底层播放速率
            _player.FrameJumpStep = (int)cbxFrameJump.SelectedItem; //根据UI设置底层跳帧
            _player.ScaleFactor = (float)Convert.ToDouble(cbxScaleRates.SelectedItem); //根据UI设置底层图像缩放
            _player.UseAlpha = IsPictureFormat(); //如果是图片，则使用透明通道
        }

        #endregion

        #region 操作

        /// <summary>
        /// 播放按钮点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnPlay_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(_currentMediaUrl))
            {
                MessageBox.Show("媒体文件不存在！");
                return;
            }
            try
            {
                if (_player.Status == MpStatus.MP_STATUS_STOPPED)
                {
                    SetMultiParams();
                }
                await _player.OpenTogglePlay(_currentMediaUrl);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 下一帧点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnNextFrame_Click(object sender, RoutedEventArgs e)
        {
            _player.NextFrame();
        }

        /// <summary>
        /// 停止点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnStop_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await _player.StopAsync();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }

        /// <summary>
        /// 静音点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnMute_Click(object sender, RoutedEventArgs e)
        {
            bool isCheck = (sender as ToggleButton).IsChecked.Value;
            _player.IsMuted = isCheck;
        }

        /// <summary>
        /// 使用关键帧定位点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkUseKeyFrameSeek_Click(object sender, RoutedEventArgs e)
        {
            _player.UseKeyFrameSeek = chkUseKeyFrameSeek.IsChecked.Value;
        }

        /// <summary>
        /// 显示图像点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkEnablePreview_Click(object sender, RoutedEventArgs e)
        {
            _player.EnablePreview = chkEnablePreview.IsChecked.Value;
        }

        /// <summary>
        /// 使用精确定位点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkUseAccuratedSeek_Click(object sender, RoutedEventArgs e)
        {
            _player.UseAccuratedSeek = chkUseAccuratedSeek.IsChecked.Value;
        }

        /// <summary>
        /// 高级选项点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAdvance_Click(object sender, RoutedEventArgs e)
        {
            bdAdvance.Visibility = btnAdvance.IsChecked.Value ? Visibility.Visible : Visibility.Collapsed;
        }

        /// <summary>
        /// 关闭高级选项
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCloseAdv_Click(object sender, RoutedEventArgs e)
        {
            bdAdvance.Visibility = Visibility.Collapsed;
            btnAdvance.IsChecked = false;
        }

        #endregion

        #region Public methods

        /// <summary>
        /// 销毁播放器
        /// </summary>
        /// <returns></returns>
        public async Task Destory()
        {
            if (!_player.IsDisposed)
            {
                await _player.StopAsync();
                _player.Dispose();
            }
        }

        public bool IsDisposed => _player.IsDisposed;

        #endregion

    }

    /// <summary>
    /// 媒体url变更事件
    /// </summary>
    public class MediaChangedEventArgs : EventArgs
    {
        public MediaChangedEventArgs(string url)
        {
            Url = url;
        }
        public string Url { get; }
    }
}
